package com.liam.websocket.netty;

import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.handler.codec.http.DefaultFullHttpResponse;
import io.netty.handler.codec.http.FullHttpRequest;
import io.netty.handler.codec.http.HttpResponseStatus;
import io.netty.handler.codec.http.HttpVersion;
import io.netty.handler.codec.http.websocketx.*;
import io.netty.util.CharsetUtil;

import java.util.Date;

/**
 * 接收处理websocket 请求业务处理类
 */
public class MyWebSocketHandler extends SimpleChannelInboundHandler<Object> {

    private WebSocketServerHandshaker handshaker;

    protected String WEB_SOCKET_URL = null;

    /**
     * 服务端处理客户端 websockt 请求的核心方法
     * @param context
     * @param msg
     * @throws Exception
     */
    @Override
    protected void messageReceived(ChannelHandlerContext context, Object msg) throws Exception {
        if(msg instanceof FullHttpRequest){
            // 客户端向服务端发起握手
            handHttpRequest(context, (FullHttpRequest) msg);
        }else if(msg instanceof WebSocketFrame){
            // websocket 链接业务
            handWebsocketFrame(context, (WebSocketFrame) msg);
        }
    }


    /**
     * 客户端与服务端websocke业务
     * @param ctx
     * @param frame
     */
    private void handWebsocketFrame(ChannelHandlerContext ctx,WebSocketFrame frame){
        if(frame instanceof CloseWebSocketFrame){
            // 关闭指令
            handshaker.close(ctx.channel(),((CloseWebSocketFrame) frame).retain());
        }else if (frame instanceof PingWebSocketFrame){
            // ping 消息
            ctx.channel().write(new PongWebSocketFrame(frame.content().retain()));
            System.out.println("ping ...");
            return;
        }

        // 是否是二进制消息 ，抛出异常
        if(! (frame instanceof TextWebSocketFrame)){
            throw new RuntimeException("目前不支持二进制消息");
        }

        // 返回应答消息
        String request = ((TextWebSocketFrame) frame).text();
        System.out.println("服务端收到：" + request);
        TextWebSocketFrame tws = new TextWebSocketFrame(new Date().toString() + ctx.channel().id()+":" + request);

        // 群发
        NettyConfig.group.writeAndFlush(tws);

    }


    /**
     * 处理客户端向服务端发起http握手请求的业务
     * @param ctx
     * @param req
     */
    private void handHttpRequest(ChannelHandlerContext ctx,FullHttpRequest req){
        if(!req.getDecoderResult().isSuccess() || !"websocket".equals(req.headers().get("Upgrade"))){
            // 如果不是http握手请求
            sendHttpResponse(ctx,req,new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.BAD_REQUEST));
            return;
        }

        // WEB_SOCKET_URL 没啥用好像
        System.err.println(WEB_SOCKET_URL+"连接地址："+req.getUri());

        // 加入
        NettyConfig.userChannelMap.put(ctx.channel().id().toString(),ctx.channel());

        WebSocketServerHandshakerFactory wsFactory = new WebSocketServerHandshakerFactory(WEB_SOCKET_URL,null,false);
        handshaker = wsFactory.newHandshaker(req);
        if(handshaker == null){
            WebSocketServerHandshakerFactory.sendUnsupportedWebSocketVersionResponse(ctx.channel());
        }else{
            handshaker.handshake(ctx.channel(),req);
        }
    }

    /**
     * 服务端向客户端响应消息
     * @param ctx
     * @param req
     * @param res
     */
    private void sendHttpResponse(ChannelHandlerContext ctx, FullHttpRequest req, DefaultFullHttpResponse res){
        if(res.getStatus().code() != 200){
            ByteBuf buf = Unpooled.copiedBuffer(res.getStatus().toString(), CharsetUtil.UTF_8);
            res.content().writeBytes(buf);
            buf.release();
        }

        // 服务端向客户端发送数据
        ChannelFuture f = ctx.channel().writeAndFlush(res);

        if(res.getStatus().code() != 200){
            f.addListener(ChannelFutureListener.CLOSE);
        }
    }


    /**
     * 异常
     * @param ctx
     * @param cause
     * @throws Exception
     */
    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        cause.printStackTrace();
        ctx.close();
        System.out.println("链接出现异常");
    }

    /**
     * 客户端与服务端创建链接
     * @param ctx
     * @throws Exception
     */
    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        NettyConfig.group.add(ctx.channel());

        System.out.println("客户端与服务端 开始链接");
    }

    /**
     * 客户端与服务端断开
     * @param ctx
     * @throws Exception
     */
    @Override
    public void channelInactive(ChannelHandlerContext ctx) throws Exception {
        NettyConfig.group.remove(ctx.channel());
        System.out.println("客户端与服务端 链接关闭");
    }

    /**
     * 服务端接收客户端发送过来的数据结束之后
     * @param ctx
     * @throws Exception
     */
    @Override
    public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
        ctx.flush();
    }
}
